Skip to content

Theme refinement and new additions#595

Merged
grantmcdermott merged 13 commits into
mainfrom
themes-redux
May 30, 2026
Merged

Theme refinement and new additions#595
grantmcdermott merged 13 commits into
mainfrom
themes-redux

Conversation

@grantmcdermott
Copy link
Copy Markdown
Owner

@grantmcdermott grantmcdermott commented May 26, 2026

Closes #409
Closes #596
Closes #597

Summary

  • Four new themes: "socviz", "broadsheet", "nber", and "web". The first obviously mimics @kjhealy's ggplot2 theme, while the latter three themes target newspaper/publication, NBER working paper, and FiveThirtyEight-style web aesthetics respectively.
  • "tufte" and "void" are now dynamic (responsive margins).
  • "ipsum" overhauled to better match hrbrthemes (bold title, no ticks, granular grid, tighter spacing, custom palette). The original variant is preserved as "ipsum2" (Improve tinytheme("ipsum")? #409).
  • "bw" and "classic" now use smaller axis text (cex.axis = 0.8), tighter spacing (gap.axis = 0.1, gap.lab = 0.4), and a more granular grid (grid = "xy") to better match their ggplot2 counterparts.

Not addressed (potential follow-ups)

  • Using the "ggplot" palette for ggplot2-inspired themes (I kept Okabe-Ito for now).
  • Font resolution helper (a la systemfonts::match_family()) for themes that prefer specific fonts (e.g., ipsum → Roboto Condensed, socviz → Source Sans 3).
  • Per-axis mgp/padj for NBER-style y-axis label positioning (labels sitting on grid lines).
  • facet.adj and facet.padding parameters for finer strip control.
  • Custom theme registration API (tinytheme_register(); Named user-defined themes not allowed in main (allowed in released version) #579).

Examples

# New and improved themes in tinyplot
pkgload::load_all("~/Documents/Projects/tinyplot")
#> ℹ Loading tinyplot

# --- Improved ipsum theme ---
# Now features bold title, no ticks, fine grid, custom palette
tinytheme("ipsum")
plt(bill_len ~ bill_dep | species, data = penguins,
    main = "Improved ipsum theme",
    sub = "Bold title, fine grid, no ticks",
    cap = "Source: Gorman et al. (2014)")

# With the nicer font (Arial Narrow)
tinytheme("ipsum", family = "Arial Narrow")
plt(bill_len ~ bill_dep | species, data = penguins,
    main = "ipsum + Arial Narrow",
    sub = "The theme's intended font pairing",
    cap = "Source: Gorman et al. (2014)")

# --- bw theme: tighter spacing to match ggplot2 ---
tinytheme("bw")
plt(bill_dep ~ bill_len | species, data = penguins,
    main = "theme_bw: tighter axis spacing",
    sub = "Smaller axis text, fine grid")

# --- New: socviz theme ---
# Inspired by Kieran Healy's socviz package
tinytheme("socviz")
plt(bill_dep ~ bill_len | species, data = penguins,
    main = "New: socviz theme",
    sub = "L-shaped axes, light grid, Okabe-Ito variant palette",
    cap = "Source: Gorman et al. (2014)")

# --- New: socviz theme ---
# With the intended font (Source Sans 3)
tinytheme("socviz", family = "Source Sans 3")
plt(bill_dep ~ bill_len | species, data = penguins,
    main = "socviz + Source Sans 3",
    sub = "The theme's intended font pairing",
    cap = "Source: Gorman et al. (2014)")

# --- New: web theme ---
# FiveThirtyEight-inspired
tinytheme("web")
plt(bill_dep ~ bill_len | species, data = penguins,
    main = "New: web theme",
    sub = "Light grey background, no axis lines",
    cap = "Data: mtcars")

# --- tufte is now dynamic ---
tinytheme("tufte")
plt(bill_dep ~ bill_len | species, data = penguins,
    main = "tufte: now with dynamic margins",
    cap = "Responsive margins reduce whitespace")

# Reset
tinytheme()

Created on 2026-05-29 with reprex v2.1.1

New themes: socviz, broadsheet, nber, web. Each targets a distinct
publication aesthetic (academic social science, newspaper, NBER working
papers, and 538-style web respectively).

Improvements to existing themes:
- ipsum: bold title, plain subtitle, no ticks, fine grid (xy),
  tighter spacing, custom palette per #408
- bw: smaller axis text (cex.axis=0.8), tighter gaps, fine grid (xy)
  to better match ggplot2 equivalent
- classic: same gap/cex adjustments as bw
- tufte/void: now dynamic (dynmar=TRUE) for responsive margins
@vincentarelbundock
Copy link
Copy Markdown
Collaborator

These are very nice, well done!

Should there be a couple more pixels between the title and subtitle?

@grantmcdermott
Copy link
Copy Markdown
Owner Author

Should there be a couple more pixels between the title and subtitle?

I'm not sure if we want to change the defaults (although perhaps for some themes...) But we can certainly try to give users more control. Currently, we use hardcoded gaps of 0.7 (plot to sub) and 0.2 (sub to main), respectively. Let me implement gap.sub and gap.main params for the dynamic plots that offer a way to adjust these.

@vincentarelbundock
Copy link
Copy Markdown
Collaborator

makes sense. TBH, htis is very low priority. Should probably just move forward with what you have now.

@zeileis
Copy link
Copy Markdown
Collaborator

zeileis commented May 28, 2026

📈 Wow, very nice. Great to see how powerful themes became!

🎨 I just wondered about the Okabe-Ito palette: When did you go with the full version (with black as the first color) and when with the reduced one?

@grantmcdermott
Copy link
Copy Markdown
Owner Author

🎨 I just wondered about the Okabe-Ito palette: When did you go with the full version (with black as the first color) and when with the reduced one?

I don' t have a super principled answer, but the gist is that I default to the full version except for a few cases where the theme I'm mimicking/borrowing from uses the 'reduced' version.

On that note: I can't find the issue right now, but some time ago we talked about dropping black as the leading colour for grouped by plots. Or, alternatively, using black as the default colour when only one by group is detected, but otherwise sticking with the "reduced" palettes (excluding black) for multi-group by plots. Is this something either of you are still interested in supporting? (I think it makes sense from a user perspective, but may require a bit of internal juggling.)

The gap between y-axis tick labels and the y-axis title previously
varied depending on label width (e.g., 1-digit vs 2-digit). Three
issues combined:

1. The whtsbp threshold used a cex-dependent value (min(1, 0.5*cex_axis))
   creating a discontinuity. Changed to fixed 0.5, which algebraically
   produces exactly gap.lab regardless of label width or theme settings.

2. The whtsbp bump was clamped to >= 0 in tinyplot.R, so narrow-label
   plots never adjusted their margin or title position. Now allows
   negative values so the margin and title shift inward together.

3. ymgp_shift (designed for cex_axis > 1) went negative for cex_axis < 1,
   pushing the title away from labels. Clamped with max(0, ...) in the
   title offset formula.

4. The no-facet dynmar path in facet.R had its own whtsbp calculation
   with a separate > 0 clamp that overrode the tinyplot.R computation.
   Removed that clamp to stay consistent.

Note: a pre-existing edge case remains where degenerate ylim (e.g.,
plt(1)) causes axisTicks to underestimate label width at pre-computation
time. This will be addressed in a follow-up.
When ylim has zero range (e.g., plt(1)), axisTicks underestimates
label width because extendrange produces a near-zero range. Expand
to ylim ± 0.5 to match what plot.window actually renders — but only
when yaxb is NULL (user-specified breaks already know the labels).

Closes #596
@grantmcdermott
Copy link
Copy Markdown
Owner Author

@vincentarelbundock I've bumped the default the spacing between the main and sub for these dynamic themes (and users can invoke gap.main and gap.sub for finer-grained control).

pkgload::load_all("~/Documents/Projects/tinyplot")
#> ℹ Loading tinyplot
tinytheme("ipsum", family = "Arial Narrow")
plt(bill_len ~ bill_dep | species, data = penguins,
    main = "ipsum + Arial Narrow",
    sub = "The theme's intended font pairing",
    cap = "Source: Gorman et al. (2014)")

Created on 2026-05-29 with reprex v2.1.1

@zeileis
Copy link
Copy Markdown
Collaborator

zeileis commented May 30, 2026

I'm not sure what the best and/or most consistent approach to handling black in palettes would be.

Also note that we currently do not handle this consistently across plot types. Some types use black as the default for single-group displays while others use the first palette color. Maybe there is also an underlying principle to this that I am overlooking?

tinytheme("clean2")
tinyplot(gear ~ 1, data = mtcars)
tinyplot(factor(gear) ~ 1, data = mtcars)
tinyplot(mpg ~ gear, data = mtcars)
tinyplot(mpg ~ factor(gear), data = mtcars)
tinyplot-color

@grantmcdermott grantmcdermott merged commit bb498fd into main May 30, 2026
3 checks passed
@grantmcdermott grantmcdermott deleted the themes-redux branch May 30, 2026 02:40
@grantmcdermott
Copy link
Copy Markdown
Owner Author

Also note that we currently do not handle this consistently across plot types. Some types use black as the default for single-group displays while others use the first palette color. Maybe there is also an underlying principle to this that I am overlooking?

Thanks @zeileis, that's actually a great example. This PR was sprawling a bit too much, so I've decided to merge as-is. We can tackle this palette issue in a separate PR/thread.

@zeileis
Copy link
Copy Markdown
Collaborator

zeileis commented May 30, 2026

Yes, definitely, this shouldn't block the work on any other Issues/PRs. I just thought that it would be a good place to mention it so that we keep it on our radars.

Regarding the example: The problem became painfully obvious to me when I prepared the "pairs plot" example in #347 (comment) where the inconsistencies in the single-group color handling are a bit distracting.

@grantmcdermott
Copy link
Copy Markdown
Owner Author

Actually, do you mind flagging these in a separate issue so we can keep track? Thanks!

@zeileis
Copy link
Copy Markdown
Collaborator

zeileis commented May 30, 2026

Done in #598

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants